package com.suray.wcs.service.plc;

import com.suray.basic.wcs.path.core.Lifter;
import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.enums.LifterMode;
import com.suray.basic.wcs.plc.enums.Location;
import com.suray.basic.wcs.plc.enums.Network;
import com.suray.basic.wcs.plc.pojo.oper.ButtonOper;
import com.suray.basic.wcs.plc.pojo.oper.lifter.LifterOper;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.enums.*;
import com.suray.wcs.service.execption.PlcExecuteException;
import com.suray.wcs.service.plc.util.LifterEventInterface;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.entity.TaskDetailDB;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.system.service.impl.TaskDetailServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 提升机调用集合类
 *
 * @author suray
 */
public class LifterEvent implements LifterEventInterface {
    protected static final Logger log = LoggerFactory.getLogger(LifterEvent.class);
    protected static final NodeDBServiceImpl nodedbServiceImpl = SpringContextHolder.getBean(NodeDBServiceImpl.class);
    protected static final TaskDetailServiceImpl taskDetailService = SpringContextHolder.getBean(TaskDetailServiceImpl.class);

    public static final Map<String, Integer> liftWord = new HashMap<>();

    /**
     * 车请求提升机第一步
     *
     * @param endLayer 目的层
     */
    public void rgvNotLoadAssignHoist1(LifterOper hoistOper, Lifter lifter, Rgv rgv, Integer plcId, int endLayer)
            throws PlcExecuteException, IOException {
        log.info("执行提升机车换层逻辑第一步,提升机内有货?: " + hoistOper.isPalletExisted(endLayer, Location.INSIDE) + "小车托盘升降状态" + rgv.getPalletStatus());
        //托盘状态下降状态时才可在提升机内有货的状态下调用
        if (hoistOper.isPalletExisted(endLayer, Location.INSIDE) && !rgv.isHasCargo()) {
            log.info("执行提升机车换层逻辑第一步,提升机内有货?: " + hoistOper.isPalletExisted(endLayer, Location.INSIDE) + "小车托盘升降状态" + rgv.getPalletStatus());
            validateAndCall(hoistOper, lifter, rgv, plcId, LifterMode.DEBUG, 0, Location.INSIDE, endLayer,
                    Location.INSIDE);
        } else {
            validateAndCall(hoistOper, lifter, rgv, plcId, LifterMode.UNLOADED, 0, Location.INSIDE, endLayer,
                    Location.INSIDE);
        }
        validateHoistArrived(hoistOper, endLayer, plcId, rgv);
        hoistOper.lockHoist(rgv.getRgvDB().getRgvNo());
        log.info(hoistOper.getDeviceName() + "上锁成功");
    }

    /**
     * 车请求提升机第二步
     *
     * @param hoistOper 提升机对象
     * @param endLayer  目的层
     */
    public void rgvNotLoadAssignHoist2(LifterOper hoistOper, Lifter lifter, Rgv rgv, Integer plcId, int endLayer)
            throws PlcExecuteException, IOException {
        originalRgvNotLoadAssignHoist2(hoistOper, lifter, rgv, plcId, endLayer);
        hoistOper.lockHoist(rgv.getRgvDB().getRgvNo());
        log.info(hoistOper.getDeviceName() + "上锁成功");
    }

    public void originalRgvNotLoadAssignHoist2(LifterOper hoistOper, Lifter lifter, Rgv rgv, Integer plcId, int endLayer)
            throws PlcExecuteException, IOException {
        log.info("执行提升机车换层逻辑第二步,车到位?: " + hoistOper.isHaveShuttle());
        long sendTime = System.currentTimeMillis();
        while (!hoistOper.isHaveShuttle()) {
            rgv.checkInterrupt("LifterEvent1", sendTime);
            log.info("执行提升机车换层逻辑第二步,车到位?: " + hoistOper.isHaveShuttle());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        validateAndCall(hoistOper, lifter, rgv, plcId, LifterMode.LOAD_RGV, 0, Location.INSIDE, endLayer,
                Location.INSIDE);
        validateHoistArrived(hoistOper, endLayer, plcId, rgv);
    }

    /**
     * 货调提升机，使用前，加锁
     *
     * @param lifterOper    提升机操作对象
     * @param lifterNode    提升机所在坐标，不是左右坐标
     * @param taskdb        当前执行的任务
     * @param startLayer    起始层
     * @param endLayer      目的层
     * @param startLocation 起始位置
     * @param endLocation   目的位置
     */
    public void palletTask(LifterOper lifterOper, Lifter lifterNode, TaskDB taskdb, Node startNode, int startLayer, int endLayer,
                           Location startLocation, Location endLocation, TaskDetailDB taskDetailDB) throws PlcExecuteException, IOException {
        log.info("任务编号" + taskdb.getTaskId() + ":货物调用提升机逻辑开始");
        log.info(lifterOper.getDeviceName() + "提升机目的位置有货?: " + lifterOper.isPalletExisted(endLayer, endLocation)
                + "提升机就绪?: " + lifterOper.isReady() + "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                + "提升机内有车?: " + lifterOper.isHaveShuttle());
        while (lifterOper.isPalletExisted(endLayer, endLocation) || !lifterOper.isReady()
                || (lifterOper.getHoistCurrentTaskNo() != 0) || lifterOper.isHaveShuttle()) {
            log.info(lifterOper.getDeviceName() + "提升机就绪?: " + lifterOper.isReady() +
                    "提升机有任务?: " + (lifterOper.getHoistCurrentTaskNo() != 0)
                    + "提升机内有车?: " + lifterOper.isHaveShuttle());
            Plc.checkPLCInterrupt(taskdb.getTaskId().toString(), "LifterEvent1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.info("锁定提升机");
        TaskDetailDB taskDetail = taskDetailService.getTaskDetail(taskdb.getTaskId(), DetailProcess.HOIST_RUN, taskDetailDB.getUuid(), taskDetailDB.getStart(), taskDetailDB.getEnding(),
                PrecessStatus.CONDUCT, null, null);
        int taskNo = taskdb.getPlcId();
        Integer code = validateAndCall(lifterOper, lifterNode, taskdb, taskNo, LifterMode.LOAD_PALLET, startLayer, startLocation,
                endLayer, endLocation);
        taskDetail.setRemark(code.toString());
        taskDetailService.updateById(taskDetail);
        log.info("货调提升机结束，等待任务结束");
        boolean isUpdated = false;
        while (lifterOper.getHoistCurrentLayer() == 0 || lifterOper.getHoistCurrentLayer() != endLayer
                || !lifterOper.isArrived() || !lifterOper.isPalletExisted(endLayer, endLocation) || lifterOper.getHoistFinalTaskNo() != taskNo) {
            Plc.checkPLCInterrupt(taskdb.getTaskId().toString(), "LifterEvent2");
            errorValidate(lifterOper);
            // 如果是出库，终点是提升机左右，则需要更新终点是否有货
            // 起始位置无货，提升机内部有货，把起始位置更新为无货状态
            if (!isUpdated && startLocation != Location.INSIDE && !lifterOper.isPalletExisted(startLayer, startLocation)
                    && lifterOper.isHaveCargo() && lifterOper.isHoistGoodArrived()) {
                Node node;
                if (TaskType.OUT.equals(taskdb.getTaskType())) {
                    node = taskdb.selectEndNode();
                } else if (TaskType.IN.equals(taskdb.getTaskType())) {
                    node = new Node(startNode.getX(), startNode.getY(), startLayer);
                } else if (TaskType.MOVE_STATION.equals(taskdb.getTaskType())) {
                    node = null;
                } else {
                    throw new RuntimeException("不合法的任务类型！");
                }
                if (node != null) {
                    TaskDetailDB startDet = taskDetailService.getTaskDetail(taskdb.getTaskId(), DetailProcess.HOIST_UPDATE_N, taskDetail.getUuid(), taskDetail.getStart(), taskDetail.getEnding(),
                            PrecessStatus.CONDUCT, node.toString(), PrecessOperation.UPDATE_N.getValue().toString());
                    nodedbServiceImpl.updateNodeDBStatus(node, NodeStatus.N.toString());
                    taskDetailService.updateTaskDetailCompleteTime(startDet);
                }
                isUpdated = true;
            }
            try {
                Thread.sleep(600);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        checkHoistTaskFinish(lifterOper, taskNo, endLayer, taskdb);
        log.info(lifterOper.getDeviceName() + "提升机终点位置有货,托盘调用提升机结束,任务号:" + taskNo);
        TaskDetailDB clearDet = taskDetailService.getTaskDetail(taskdb.getTaskId(), DetailProcess.CLEAR_HOIST_TASK, taskDetail.getUuid(), taskDetail.getStart(), taskDetail.getEnding(),
                PrecessStatus.CONDUCT, null, null);
        lifterOper.setHoistFinalTaskNoClear();
        LifterEvent.liftWord.remove(lifterOper.getLockLocation().toString());
        taskDetailService.updateTaskDetailCompleteTime(clearDet);
        taskDetailService.updateTaskDetailCompleteTime(taskDetail);
    }

    /**
     * 发送小车到位信号
     */
    public void sendShuttleArrived(LifterOper hoistOper) throws IOException {
        hoistOper.setHoistShuttleArrived();
    }

    /**
     * 发送小车驶离信号
     */
    public void sendShuttleLeave(LifterOper hoistOper) throws IOException {
        hoistOper.setHoistShuttleOuted();
    }

    /**
     * 调用提升机前的安全验证
     *
     * @param taskNo       发给PLC的编号
     * @param lifterMode   模式
     * @param objFloor1    起始层
     * @param objLocation1 起始位置
     * @param objFloor2    目标层
     * @param objLocation2 目标层
     */
    public Integer validateAndCall(LifterOper hoistOper, Lifter lifter, Object obj, int taskNo,
                                   LifterMode lifterMode, int objFloor1, Location objLocation1, int objFloor2, Location objLocation2)
            throws PlcExecuteException, IOException {
        // 最大值为99
        Integer code = validateParams(hoistOper, lifterMode, objFloor1, objFloor2, objLocation1, objLocation2, obj);
        hoistOper.callLift(code, taskNo);
        LifterEvent.liftWord.put(hoistOper.getLockLocation().toString(), code);
        log.info("调用提升机任务字：" + code + "任务编号" + taskNo);
        return code;
    }

    protected Integer validateParams(LifterOper hoistOper, LifterMode lifterMode, int objFloor1, int objFloor2, Location objLocation1, Location objLocation2, Object obj) {
        if (objFloor1 > 99 || objFloor1 < 0 || objFloor2 < 0 || objFloor2 > 99) {
            throw new RuntimeException("提升机层参数异常");
        }
        long sendTime = System.currentTimeMillis();
        log.info("当前模式" + lifterMode + "提升机就绪?: " + hoistOper.isReady() + "提升机无任务?: "
                + (hoistOper.getHoistCurrentTaskNo() == 0) + "提升机内有车?: " + hoistOper.isHaveShuttle());
        while (!validate(hoistOper, lifterMode)) {
            if (obj instanceof Rgv) {
                Rgv rgv = (Rgv) obj;
                rgv.checkInterrupt("LifterEvent2", sendTime);
            }
            if (obj instanceof TaskDB) {
                TaskDB taskDB = (TaskDB) obj;
                Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "LifterEvent3");
            }
            if (obj instanceof ButtonOper) {
                ((ButtonOper) obj).interrupt();
            }
            errorValidate(hoistOper);
            log.info(!validate(hoistOper, lifterMode) + "");
            log.info("当前模式" + lifterMode + "提升机就绪?: " + hoistOper.isReady() + "提升机无任务?: "
                    + (hoistOper.getHoistCurrentTaskNo() == 0) + "提升机内有车?: " + hoistOper.isHaveShuttle());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (lifterMode.getModeNum() == LifterMode.RGV.getModeNum()) {
            objFloor1 = 0;
            objLocation1 = Location.INSIDE;
            objLocation2 = Location.INSIDE;
        }
        if (lifterMode.getModeNum() == LifterMode.UNLOADED.getModeNum()
                || lifterMode.getModeNum() == LifterMode.LOAD_RGV.getModeNum()) {
            objFloor1 = 0;
            objLocation1 = Location.INSIDE;
            objLocation2 = Location.INSIDE;
        }
        if (lifterMode.getModeNum() == LifterMode.LOAD_PALLET.getModeNum() && objLocation1 == Location.INSIDE) {
            objFloor1 = hoistOper.getHoistCurrentLayer();
        }
        return lifterMode.getModeNum() * 1000000 + objFloor1 * 10000 + objLocation1.getCode() * 1000 + objFloor2 * 10
                + objLocation2.getCode();
    }

    public boolean validate(LifterOper hoistOper, LifterMode lifterMode) {
        boolean assignFlag = hoistOper.getPlc().getPlcStatus() != Network.OFF_LINE && hoistOper.isReady()
                && hoistTaskIsHave(hoistOper.getHoistCurrentTaskNo()) && !hoistOper.isHaveShuttle() && !hoistOper.lockState();
        if (lifterMode == LifterMode.LOAD_RGV) {
            assignFlag = hoistOper.getPlc().getPlcStatus() != Network.OFF_LINE && hoistOper.isReady()
                    && hoistTaskIsHave(hoistOper.getHoistCurrentTaskNo());
        }
        //提升机四向车模式下验证提升机离线、就绪、任务是否完成
        if (lifterMode == LifterMode.RGV) {
            assignFlag = hoistOper.getPlc().getPlcStatus() != Network.OFF_LINE && hoistOper.isReady()
                    && hoistTaskIsHave(hoistOper.getHoistCurrentTaskNo());
        }
        //两向车任务提升机未离线提升机就绪提升机当前任务为空
        if (lifterMode == LifterMode.TWO_DIRECTION_RGV) {
            assignFlag = hoistOper.getPlc().getPlcStatus() != Network.OFF_LINE && hoistOper.isReady()
                    && hoistTaskIsHave(hoistOper.getHoistCurrentTaskNo());
        }
        if (lifterMode == LifterMode.UNLOADED) {
            assignFlag = hoistOper.getPlc().getPlcStatus() != Network.OFF_LINE && hoistOper.isReady()
                    && hoistTaskIsHave(hoistOper.getHoistCurrentTaskNo()) && !hoistOper.isHaveShuttle()
                    //因为检测的是提升机中,所以目的层固定不影响
                    && !hoistOper.isPalletExisted(1, Location.INSIDE);
        }

        if (hoistOper.lockState()) {
            LoggerUtil.info("提升机已锁定，请解锁", LifterEvent.class);
            assignFlag = false;
        }

        return assignFlag;
    }

    /**
     * 提升机到达目标层验证
     */
    public void validateHoistArrived(LifterOper hoistOper, int endLayer, int taskNo, Rgv rgv) throws PlcExecuteException {
        log.info(hoistOper.getDeviceName() + "提升机到位?: " + hoistOper.isArrived() + ", 当前层: "
                + hoistOper.getHoistCurrentLayer() + ", 需求到达层: " + endLayer + ", 需求完成编号:" + taskNo);
        long sendTime = System.currentTimeMillis();
        while (!hoistOper.isArrived() || hoistOper.getHoistCurrentLayer() != endLayer || hoistOper.getHoistFinalTaskNo() != taskNo) {
            rgv.checkInterrupt("LifterEvent3", sendTime);
            errorValidate(hoistOper);
            log.info(hoistOper.getDeviceName() + "提升机到位?: " + hoistOper.isArrived() + ", 当前层: "
                    + hoistOper.getHoistCurrentLayer() + ", 需求到达层: " + endLayer + ", 需求完成编号:" + taskNo);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 检测当前提升机任务是否完成
     */
    public void checkHoistTaskFinish(LifterOper hoistOper, int taskId, int endLayer, TaskDB taskDB) {
        log.info("等待" + hoistOper.getDeviceName() + "任务结束，任务号:" + taskId + "目标层" + endLayer);
        while (hoistOper.getHoistFinalTaskNo() != taskId || hoistOper.getHoistCurrentLayer() != endLayer) {
            Plc.checkPLCInterrupt(taskDB.getTaskId().toString(), "LifterEvent4");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 提升机故障问题验证，出现该问题时无法通过等待获取资源 此时自动结束当前任务
     */
    public void errorValidate(LifterOper hoistOper) {
        errorOriginalValidate(hoistOper);
    }

    @Override
    public boolean isPalletExisted(LifterOper lifterOper, Integer endLayer, Location endLocation) {
        return lifterOper.isPalletExisted(endLayer, endLocation);
    }

    public void errorOriginalValidate(LifterOper hoistOper) {
        if (hoistOper.isHoistFontLimited()) {
            log.error(ResultEnum.提升机前限位超限.toString());
        }
        if (hoistOper.isHoistBackLimited()) {

            log.error(ResultEnum.提升机后限位超限.toString());
        }
        if (hoistOper.isHoistUpLimited()) {

            log.error(ResultEnum.提升机上限为超限.toString());
        }
        if (hoistOper.isHoistDownLimited()) {

            log.error(ResultEnum.提升机下限为超限.toString());
        }
        if (hoistOper.isHoistBarReadCorrect()) {

            log.error(ResultEnum.提升机条码读取故障.toString());
        }
        if (hoistOper.isHoistBarRangeCorrect()) {

            log.error(ResultEnum.提升机条码范围异常.toString());
        }
        if (hoistOper.isHoistTaskCodeCorrect()) {
            log.error(ResultEnum.提升机任务字非法.toString());
        }
        if (hoistOper.isHoistStartEmpty()) {

            log.error(ResultEnum.提升机起始位置无货.toString());
        }
        if (hoistOper.isHoistEndEmpty()) {

            log.error(ResultEnum.提升机终点位置有货.toString());
        }
        if (hoistOper.isHoistInStock()) {

            log.error(ResultEnum.提升机内有货.toString());
        }
        if (hoistOper.isHoistShuttleArrived()) {

            log.error(ResultEnum.提升机内小车未到位.toString());
        }
        if (hoistOper.isHoistShuttleOuted()) {

            log.error(ResultEnum.提升机内小车未驶离.toString());
        }
        if (hoistOper.notReadyHoist()) {
            log.error(ResultEnum.提升机未就绪时wcs下发任务.toString());
        }
        if (hoistOper.lockProblem()) {
            log.error(ResultEnum.锁定失败.toString());
        }
        if (hoistOper.unlockProblem()) {
            log.error(ResultEnum.解锁失败.toString());
        }
        if (hoistOper.disconnect()) {
            log.error(ResultEnum.与主控交互断开.toString());
        }
        if (hoistOper.chainFault()) {
            log.error(ResultEnum.链条机故障.toString());
        }
        if (hoistOper.rollerTableFault()) {
            log.error(ResultEnum.辊道机故障.toString());
        }
        if (hoistOper.transplantLiftFailure()) {
            log.error(ResultEnum.移栽升降机故障.toString());
        }
        if (hoistOper.isEdgeTransfinite()) {
            log.error(ResultEnum.对接链条机超限.toString());
        }

        if (hoistOper.isChainBackError()) {
            log.error(ResultEnum.提升机内链条机后边缘超限.toString());
        }
        if (hoistOper.isChainFontError()) {
            log.error(ResultEnum.提升机内链条机前边缘超限.toString());
        }
    }

    private boolean hoistTaskIsHave(int number) {
        return number == 0;
    }
}
